using Gee;
using Gtk;

namespace Ribbons {

	/** ToolBox containing several widgets displayed in rows. */
	public class ToolBox : Container {

		private Gee.List<Widget> _widgets;
		private Gtk.Requisition[] _requisitions;

		/** Gets or sets the spacing between children. */
		private int _spacing;
		public int spacing {
			set {
				_spacing = value;
				queue_draw ();
			}
			get { return _spacing; }
		}

		/** Construction method */
		construct {
			_widgets = new Gee.ArrayList<Widget> ();

			set_flags (get_flags () | WidgetFlags.NO_WINDOW);

			add_events (Gdk.EventMask.BUTTON_PRESS_MASK
			          | Gdk.EventMask.BUTTON_RELEASE_MASK
			          | Gdk.EventMask.POINTER_MOTION_MASK);

			_spacing = 2;
		}
		
		/**
		 * Adds a widget before all existing widgets.
		 *
		 * @param widget  The widget to add.
		 */
		public void prepend (Widget widget) {
			insert (widget, 0);
		}
		
		/**
		 * Adds a widget after all existing widgets.
		 *
		 * @param widget  The widget to add.
		 */
		public void append (Widget widget) {
			insert (widget, -1);
		}
		
		/**
		 * Inserts a widget at the specified location.
		 *
		 * @param widget  The widget to add.
		 * @param index   The index (starting at 0) at which the widget must
		 *                be inserted, or -1 to insert the widget after all
		 *                existing widgets.
		 */
		public void insert (Widget widget, int index) {
			widget.set_parent (this);
			widget.visible = true;

			if (index == -1) {
				_widgets.add (widget);
			} else {
				_widgets.insert (index, widget);
			}

			show_all ();
		}

		/**
		 * Removes the widget at the specified index.
		 *
		 * @param index  Index of the widget to remove.
		 */
		public void remove_widget (int index) {		// XXX-GEETK name collision workaround
			_widgets[index].parent = null;

			if (index == -1) {
				_widgets.remove_at (_widgets.size - 1);
			} else {
				_widgets.remove_at (index);
			}
			
			show_all ();
		}

		protected override void forall_internal (bool include_internals,
		                                         Gtk.Callback callback)
		{
			foreach (var widget in _widgets) {
				if (widget.visible) {
					callback (widget);
				}
			}
		}

		protected override void size_request (out Requisition requisition) {
			base.size_request (out requisition);

			if (_requisitions == null || _requisitions.length != _widgets.size) {
				_requisitions = new Gtk.Requisition[_widgets.size];
			}

			int total_width = 0;
			int row_height = 0;
			foreach (var widget in _widgets) {
				if (widget.visible) {
					Requisition req;
					widget.size_request (out req);
					row_height = int.max (row_height, req.height);
				}
			}

			int i = 0;
			foreach (var widget in _widgets) {
				if (widget.visible) {
					widget.height_request = row_height;
					Requisition req;
					widget.size_request (out req);
					_requisitions[i] = req;
					total_width += _requisitions[i].width;
				}
				++i;
			}

			if (this.width_request != -1 && this.height_request != -1) {
				requisition.width = this.width_request;
				requisition.height = this.height_request;
			} else if (this.width_request != -1) {
				int total_height = row_height;
				int cur_width = 0;
				int avail_width = this.width_request - 2 * (int) this.border_width;

				i = 0;
				foreach (var widget in _widgets) {
					if (widget.visible) {
						Gtk.Requisition r = _requisitions[i];

						if (cur_width == 0 || cur_width + r.width <= avail_width) {
							// Continue current line
							cur_width += r.width;
							if (cur_width != 0) {
								cur_width += _spacing;
							}
						} else {
							// Start new line
							total_height += row_height + _spacing;
							cur_width = 0;
						}
					}
					++i;
				}

				requisition.width = this.width_request;
				requisition.height = total_height + 2 * (int) this.border_width;
			} else {
				int rows_left = (int) Math.floor ((double) (this.height_request + _spacing)
				                                / (double) (row_height + _spacing));
				if (rows_left == 0) {
					rows_left = 1;
				}
				int width_left = total_width;
				int cur_width = 0;
				int max_width = 0;
				int min_width = width_left / rows_left;
				
				i = 0;
				int current_widget_counter = 0;
				foreach (var widget in _widgets) {
					if (widget.visible) {
						Gtk.Requisition r = _requisitions[i];

						width_left -= r.width;
						cur_width += r.width;
						++current_widget_counter;

						if (cur_width >= min_width) {
							// Start new line
							cur_width += (current_widget_counter - 1) * _spacing;
							max_width = int.max (max_width, cur_width);
							cur_width = 0;
							--rows_left;
							if (rows_left == 0) {
								break;
							}
							min_width = width_left / rows_left;
							current_widget_counter = 0;
						}
					}
					++i;
				}

				requisition.width = max_width + 2 * (int) this.border_width;

				if (this.height_request == -1) {
					requisition.height = row_height;
				} else {
					requisition.height = this.height_request;
				}
			}
		}

		protected override void size_allocate (Gdk.Rectangle allocation) {
			base.size_allocate (allocation);

			int right = allocation.x + allocation.width - (int) this.border_width;
			int left = allocation.x + (int) this.border_width;
			int bottom = allocation.y + allocation.height - (int) this.border_width;
			int x = left;
			int row_y = allocation.y + (int) this.border_width;
			int max_height = 0;

			int i = 0;
			foreach (var widget in _widgets) {
				if (widget.visible) {
					Gdk.Rectangle r = Gdk.Rectangle ();
					r.width = _requisitions[i].width;
					r.height = _requisitions[i].height;

					if (x > left && x + r.width > right) {
						row_y += max_height + _spacing;
						max_height = 0;
						x = left;
					}

					r.x = x;
					r.y = row_y;
					r.width = int.min (right, r.x + r.width) - r.x;
					r.height = int.min (bottom, r.y + r.height) - r.y;
					widget.size_allocate (r);

					x += r.width + _spacing;
					max_height = int.max (max_height, r.height);
				}
				++i;
			}
		}

		internal override void remove (Widget widget) {
			// XXX-GEETK
		}
	}
}

